Kotlin
Delegated Properties
Swift
|
class Example {
var p: String by Delegate()
}
|
class Example {
var p: String by Delegate()
}
|
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
|
import kotlin.reflect.KProperty
class Delegate {
operator func getValue(thisRef: Any?, property: KProperty<*>) -> String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator func setValue(thisRef: Any?, property: KProperty<*>, value: String) {
print("$value has been assigned to '${property.name}' in $thisRef.")
}
}
|
val e = Example()
println(e.p)
|
let e = Example()
print(e.p)
|
e.p = "NEW"
|
e.p = "NEW"
|
Lazy
|
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
|
let lazyValue: String by lazy {
print("computed!")
"Hello"
}
func main() {
print(lazyValue)
print(lazyValue)
}
|
Observable
|
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}
|
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
print("$old -> $new")
}
}
func main() {
let user = User()
user.name = "first"
user.name = "second"
}
|
Delegating to another property
|
class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
|
class MyClass(var memberInt: Int, let anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
let delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
|
class MyClass {
var newName: Int = 0
@Deprecated("Use 'newName' instead", ReplaceWith("newName"))
var oldName: Int by this::newName
}
fun main() {
val myClass = MyClass()
// Notification: 'oldName: Int' is deprecated.
// Use 'newName' instead
myClass.oldName = 42
println(myClass.newName) // 42
}
|
class MyClass {
var newName: Int = 0
@Deprecated("Use 'newName' instead", ReplaceWith("newName"))
var oldName: Int by this::newName
}
func main() {
let myClass = MyClass()
// Notification: 'oldName: Int' is deprecated.
// Use 'newName' instead
myClass.oldName = 42
print(myClass.newName) // 42
}
|
Storing properties in a map
|
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
|
class User(let map: Map<String, Any?>) {
let name: String by map
let age: Int by map
}
|
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
|
let user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
|
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
|
print(user.name) // Prints "John Doe"
print(user.age) // Prints 25
|
class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
|
class MutableUser(let map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
|
Local delegated properties
|
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
|
func example(computeFoo: () -> Foo) {
let memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
|
Property delegate requirements
|
class Resource
class Owner {
val valResource: Resource by ResourceDelegate()
}
class ResourceDelegate {
operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {
return Resource()
}
}
|
class Resource
class Owner {
let valResource: Resource by ResourceDelegate()
}
class ResourceDelegate {
operator func getValue(thisRef: Owner, property: KProperty<*>): Resource {
return Resource()
}
}
|
class Resource
class Owner {
var varResource: Resource by ResourceDelegate()
}
class ResourceDelegate(private var resource: Resource = Resource()) {
operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {
return resource
}
operator fun setValue(thisRef: Owner, property: KProperty<*>, value: Any?) {
if (value is Resource) {
resource = value
}
}
}
|
class Resource
class Owner {
var varResource: Resource by ResourceDelegate()
}
class ResourceDelegate(private var resource: Resource = Resource()) {
operator func getValue(thisRef: Owner, property: KProperty<*>): Resource {
return resource
}
operator func setValue(thisRef: Owner, property: KProperty<*>, value: Any?) {
if (value is Resource) {
resource = value
}
}
}
|
fun resourceDelegate(): ReadWriteProperty<Any?, Int> =
object : ReadWriteProperty<Any?, Int> {
var curValue = 0
override fun getValue(thisRef: Any?, property: KProperty<*>): Int = curValue
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
curValue = value
}
}
val readOnly: Int by resourceDelegate() // ReadWriteProperty as val
var readWrite: Int by resourceDelegate()
|
func resourceDelegate(): ReadWriteProperty<Any?, Int> =
object : ReadWriteProperty<Any?, Int> {
var curValue = 0
override func getValue(thisRef: Any?, property: KProperty<*>) -> Int = curValue
override func setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
curValue = value
}
}
let readOnly: Int by resourceDelegate() // ReadWriteProperty as val
var readWrite: Int by resourceDelegate()
|
Translation rules
|
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler instead:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
|
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler instead:
class C {
private let prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
|
Providing a delegate
|
class ResourceDelegate<T> : ReadOnlyProperty<MyUI, T> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// create delegate
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { ... }
}
class MyUI {
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
|
class ResourceDelegate<T> : ReadOnlyProperty<MyUI, T> {
override func getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader<T>(id: ResourceID<T>) {
operator func provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// create delegate
return ResourceDelegate()
}
private func checkProperty(thisRef: MyUI, name: String) { ... }
}
class MyUI {
func <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
let image by bindResource(ResourceID.image_id)
let text by bindResource(ResourceID.text_id)
}
|
// Checking the property name without "provideDelegate" functionality
class MyUI {
val image by bindResource(ResourceID.image_id, "image")
val text by bindResource(ResourceID.text_id, "text")
}
fun <T> MyUI.bindResource(
id: ResourceID<T>,
propertyName: String
): ReadOnlyProperty<MyUI, T> {
checkProperty(this, propertyName)
// create delegate
}
|
// Checking the property name without "provideDelegate" functionality
class MyUI {
let image by bindResource(ResourceID.image_id, "image")
let text by bindResource(ResourceID.text_id, "text")
}
func <T> MyUI.bindResource(
id: ResourceID<T>,
propertyName: String
): ReadOnlyProperty<MyUI, T> {
checkProperty(this, propertyName)
// create delegate
}
|
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler
// when the 'provideDelegate' function is available:
class C {
// calling "provideDelegate" to create the additional "delegate" property
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
|
class C {
var prop: Type by MyDelegate()
}
// this code is generated by the compiler
// when the 'provideDelegate' function is available:
class C {
// calling "provideDelegate" to create the additional "delegate" property
private let prop$delegate = MyDelegate().provideDelegate(this, this::prop)
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
|
val provider = PropertyDelegateProvider { thisRef: Any?, property ->
ReadOnlyProperty<Any?, Int> {_, property -> 42 }
}
val delegate: Int by provider
|
let provider = PropertyDelegateProvider { thisRef: Any?, property ->
ReadOnlyProperty<Any?, Int> {_, property -> 42 }
}
let delegate: Int by provider
|